home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Communication / NewsBase / Source / INewsGroupTreeControl.m < prev    next >
Text File  |  1993-01-12  |  24KB  |  787 lines

  1. /*$Copyright:
  2.  * Copyright (C) 1992.5.22. Recruit Co.,Ltd. 
  3.  * Institute for Supercomputing Research
  4.  * All rights reserved.
  5.  * NewsBase  by ISR, Kazuto MIYAI, Gary ARAKAKI, Katsunori SUZUKI, Kok-meng Lue
  6.  *
  7.  * You may freely copy, distribute and reuse the code in this program under 
  8.  * following conditions.
  9.  * - to include this notice in the source code, if it is to be distributed 
  10.  *   with source code.
  11.  * - to add the file named "COPYING" within the code, which shall include 
  12.  *   GNU GENERAL PUBLIC LICENSE(*).
  13.  * - to display an acknowledgement in binary code as follows: "This product
  14.  *   includes software developed by Recruit Co.,Ltd., ISR."
  15.  * - to display a notice which shall state that the users may freely copy,
  16.  *   distribute and reuse the code in this program under GNU GENERAL PUBLIC
  17.  *   LICENSE(*)
  18.  * - to indicate the way to access the copy of GNU GENERAL PUBLIC LICENSE(*)
  19.  *
  20.  *   (*)GNU GENERAL PUBLIC LICENSE is stored in the file named COPYING
  21.  * 
  22.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  23.  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  24.  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  25. $*/
  26. /*
  27.  *    INewsGroupTreeControl is a subclass of the INNTP class.
  28.  *    It initializes and maintains the newsgroup tree.
  29.  *     It also handles the posting of articles.
  30.  *    It handles all communication with the NNTP server.
  31.  */
  32.  
  33. #import <appkit/appkit.h>
  34. #import <streams/streams.h>
  35. #import <objc/zone.h>
  36. #import <mach/mach.h>
  37. #import <defaults/defaults.h>
  38. #import <strings.h>
  39. #import <libc.h>
  40. #import <ctype.h>
  41. #import <sys/types.h>
  42. #import <sys/stat.h>
  43. #import <errno.h>
  44. #import <dpsclient/dpsNeXT.h>
  45.  
  46. #import "INewsGroupTreeControl.h"
  47. #import "data_types.h"
  48. #import "response_codes.h"
  49. #import "InfoD.h"
  50. #import "INewsRc.h"
  51. #import "INewsgroupInfoD.h"
  52. #import "ITreeNodeD.h"
  53. #import "IReceiveSpeaker.h"
  54. #import "errdebug.h"
  55.  
  56. #import "Localization.h"
  57.  
  58. #define LoStr(key)      doLocalString(NULL,key,NULL)
  59.  
  60. #define OWNER "NewsBase"
  61. #define NNTPSERVER "NNTPServer"
  62. //#define MAX_NO_OF_TOKENS 256
  63. //#define        OK_GROUPS       215     /* Newsgroups follow */
  64.  
  65. id currentNewsGroup;
  66. id currentArticle;
  67.  
  68. @implementation INewsGroupTreeControl
  69.  
  70. - initServer:(char *)nntpHost allNews:(BOOL)allNewsFlag
  71. {    
  72.     /* connect to nntp server */
  73.     if ([self openServer:nntpHost]==nil) {
  74.     return nil;
  75.     }
  76.     /* set rootName to IOmodule, this will be used for the root name */
  77.     /* on newsgroup browser */
  78.     [self setRootName:nntpHost];
  79.     
  80.     // set flag for reading all news group or subscribed only
  81.     iAllNewsFlag = allNewsFlag;
  82.     
  83.     /* make newsgroup tree zone */
  84.     newsgroupTreeZone = NXCreateZone (vm_page_size, vm_page_size, YES);
  85.  
  86.     /* read .newsrc file and make string table */
  87.     if ([self readNewsRcFile] ==NULL) {
  88.         return NULL;
  89.     }
  90.     iNewsGroupTreeRoot = [[ITreeNodeD allocFromZone:
  91.                 (NXZone *)newsgroupTreeZone] initWithKey:"root"];
  92.     
  93.     // make news group tree
  94.     [self listAndMakeNewsgroupTree];
  95.     
  96.     // start auto reconnecting timed entry event
  97.     [self setTimedEntryForReconnect];
  98.     return self;
  99. }
  100.  
  101. - listAndMakeNewsgroupTree
  102. {
  103.     int        statusCode;
  104.     id        groupInfoD;
  105.     char    groupName[256], canPost[2];
  106. //    char    groupnameTmp[256];
  107.     char    *groupnameTmp;
  108.     int        last, first;
  109.     char    ch, tempbuf[LINE_BUFFER_SIZE];
  110.     char    *key[MAX_NO_OF_TOKENS];
  111.     ITreeNodeD    *node;
  112.     void    makeTreeKey(const char *, const char **, const char *);
  113.  
  114.     statusCode = [self issueCommand:"list"];
  115.     if (statusCode != OK_GROUPS) {
  116.     NXRunAlertPanel([self name],LoStr("list command failed"),
  117.         LoStr("OK"),NULL,NULL);
  118.         [NXApp terminate:self];
  119.     }
  120.     canPost[1] = '\0';
  121.     while ((ch = fgetc(inntpFile)) !='.') {
  122.     ungetc (ch, inntpFile);
  123.     fscanf (inntpFile, "%[^\r]\r\n", tempbuf);
  124.     sscanf (tempbuf, "%255s %d %d %c", groupName, &last, &first, &canPost);
  125.     
  126.     /* check canPost is '='(alias) */
  127.     if (canPost[0] == '=') {
  128.         /* if the newsgroup is aliased, skip this group */
  129.         continue;
  130.     }
  131.     /* check group name */
  132.     /* Subscribe and NotExist -> make node for news group tree */
  133.     /* UnSubscribe           -> not to make into tree */
  134.     switch ((NewsStat)[iNewsRc isSubscribe:groupName]) {
  135.         case UnSubscribe:
  136.         if (iAllNewsFlag == NO) {
  137.         /* unsubscribed newsgroup is marked as not-active */
  138.         /* only subscribed newsgroup, so skip unsubscribed one */
  139.             continue;
  140.         }
  141.         /* iAllNewsFlag == YES */
  142.         break;
  143.         case NotExist:
  144.         fprintf (stderr, 
  145.             "WARNING: new group \"%s\" is added\n", groupName);
  146.         case Subscribe:
  147.         break;
  148.     }
  149.     
  150.     // if iNewsGroupTreeRoot already had the node for key, 
  151.     // the node has already been created. if not, make it.
  152.     //strncpy(groupnameTmp, groupName, sizeof(groupnameTmp)-1);
  153.     groupnameTmp = NXCopyStringBuffer(groupName);
  154.     makeTreeKey(groupnameTmp, key, ".");
  155.     if ((node=[iNewsGroupTreeRoot nodeForKey:key]) == nil) {
  156.         node = [iNewsGroupTreeRoot addNodeForKey:key];
  157.     }
  158.     free(groupnameTmp);
  159.     
  160.     if((groupInfoD = [node dataForKey:GROUPINFO]) == nil) {
  161.         /* node does not have groupInfoD, so initialize it */
  162.         /* when launching NewsBase first, this routine will be called, */
  163.         /* but not entering when reconnecting. */
  164.         groupInfoD = [[INewsgroupInfoD allocFromZone:
  165.             (NXZone *)newsgroupTreeZone] initWithKey:GROUPINFO];
  166.         [groupInfoD addInfo:groupName key:GROUPNAME];
  167.         [node insertKeyedObject:groupInfoD];
  168.  
  169.         /* set other info. from header field */
  170.         // set FIRST, CANPOST, ART_NUM_SET when launched
  171.         // skip these when reconnecting
  172.         [groupInfoD addInfoInt:first key:FIRST];
  173.         [groupInfoD addInfo:canPost key:CANPOST];
  174.         /* set ART_NUM_SET */
  175.         /* "addInfoInt: key:FIRST" will clean up ART_NUM_SET, */
  176.         /* but response of list command includes poor infomation about */
  177.         /* FIRST, so locate ART_NUM_SET after "addInfoInt: key:FIRST" */
  178.         /* not to waste cpu */
  179.         [iNewsRc setGroupInfo:groupInfoD];
  180.     }
  181.  
  182.     // LAST will be reset when reconnecting
  183.     [groupInfoD addInfoInt:last key:LAST];
  184.     }
  185.  
  186.     fgetc (inntpFile);                    /* get \r */
  187.     fgetc (inntpFile);                    /* get \n */
  188.     /* move file pointer to the end of stream */
  189.     fseek (inntpFile, (long)0, SEEK_END);
  190.  
  191.     return self;
  192. }
  193.  
  194. - newsGroupTreeRoot
  195. {
  196.     return iNewsGroupTreeRoot;
  197. }
  198.  
  199. - (const char *)currentNewsgroupName
  200. {
  201. //    return [[iCurrentNewsgroup link] key];
  202.     return [[iCurrentNewsgroup dataForKey:GROUPINFO] infoForKey:GROUPNAME];
  203. }
  204.  
  205. - currentArticleItem
  206. {
  207.     return iCurrentArticleItem;
  208. }
  209.  
  210. - initNewsGroup:newsGroup readFlag:(ReadFlag)rflag
  211. {
  212.     char        combuf[LINE_CHR_MAX];
  213.     id groupInfoD;
  214.     int        statusCode;
  215.     int         dum, estNum, first, last, tmpint;
  216.     int i;
  217.     id articleDB;
  218.     int        artWillGetNum, artNumAmount;
  219.        
  220.     /* headersZone is for ItemHeaderBrowser */
  221.     /* when newsgroup is switched, headers are no longer needed */
  222.     if (headersZone != NULL) {
  223.         NXDestroyZone(headersZone);
  224.     }
  225.     headersZone = NXCreateZone (vm_page_size, vm_page_size, YES);
  226.     DBG(1, fprintf(stderr, "headersZone = %x\n", (unsigned int)headersZone););
  227.  
  228.     articleDB = [[IOrderedListD allocFromZone:headersZone]
  229.                     initWithKey:"articleDB"];
  230.     groupInfoD = [newsGroup dataForKey:GROUPINFO];
  231.  
  232.     strncpy (combuf, "group ", 7);
  233.     strncat (combuf, (char *)[groupInfoD infoForKey:GROUPNAME], (LINE_CHR_MAX-7));
  234.  
  235.     statusCode = [self issueCommand:combuf];
  236.     if (statusCode != OK_GROUP) {
  237.         if (statusCode == ERR_NOGROUP) {
  238.             NXRunAlertPanel(LoStr("WARNING"),
  239.         LoStr("InntpTalk: no such newsgroup"),LoStr("OK"),NULL,NULL);
  240.         } else {
  241.             NXRunAlertPanel(LoStr("ERROR"),
  242.         LoStr("InntpTalk: group command failed"),LoStr("OK"),NULL,NULL);
  243.         fseek (inntpFile, (long)0, SEEK_END);
  244.         return nil;
  245.         }
  246.     }
  247.     sscanf (iresponse, "%d %d %d %d", &dum, &estNum, &first, &last);
  248.                                                 /* dum is status code */
  249.     tmpint = (int)[groupInfoD infoForKey:LAST];
  250.     if (tmpint != last) {
  251.         [groupInfoD addInfoInt:last key:LAST];
  252.     }
  253.     tmpint = (int)[groupInfoD infoForKey:FIRST];
  254.     if (tmpint != first) {
  255.         [groupInfoD addInfoInt:first key:FIRST];
  256.     }
  257.     [groupInfoD addInfoInt:estNum key:ESTIMATENUM];
  258.    
  259.     fseek (inntpFile, (long)0, SEEK_END);
  260.  
  261.     /* # of articles on server */
  262.     
  263.     /* read check */   
  264.     switch ( rflag ) {
  265.     case ALL:
  266.     for (i=first; i<=last; i++) {
  267.         [self commandHead:i articleDB:articleDB];
  268.     }
  269.     break;
  270.     case UNMARKEDONLY:
  271.     default:
  272.     /* default # of getting article from server is in NUM_ARTTOGET */
  273.     if ((artNumAmount=[groupInfoD countArticleUnMarked])
  274.             > (int)atoi(NXGetDefaultValue(OWNER,NUM_ARTTOGET))) {
  275.  
  276.         // a lot of article is to be read in server,
  277.         // make alert panel and run modal roop
  278.         NXModalSession theSession;
  279.         int modalStatus;
  280.         
  281.         [oGetArticleNumField setIntValue:artNumAmount];
  282.         [iPercentageView resetValue];
  283.         [oGetArticleNumWindow makeKeyAndOrderFront:self];
  284.  
  285.         [NXApp beginModalSession:&theSession for:oGetArticleNumWindow];
  286.         while ((modalStatus = [NXApp runModalSession:&theSession]) 
  287.                                 == NX_RUNCONTINUES) {
  288.         ;
  289.         }
  290.         if (modalStatus != 1) {
  291.         // cancel button was clicked
  292.         goto endModal;
  293.         }
  294.         artWillGetNum = [oGetArticleNumField intValue];
  295.         if (artWillGetNum == 0) {
  296.         goto endModal;
  297.         }
  298.         
  299.         // once stopModal is issued we have to restart modal loop?????
  300.         [NXApp endModalSession:&theSession];
  301.         [NXApp beginModalSession:&theSession for:oGetArticleNumWindow];
  302.         modalStatus = [NXApp runModalSession:&theSession];
  303.  
  304.         first = [groupInfoD firstArticleForAmount:artWillGetNum];
  305.         [iPercentageView setMin:(float)first max:(float)last];
  306.         DBG(1,fprintf(stderr,"    first=%d\n",first));
  307.         for (i=first; i<=last && modalStatus == NX_RUNCONTINUES; i++) {
  308.         // cancel or ok button will break modal loop
  309.         modalStatus = [NXApp runModalSession:&theSession];
  310.  
  311.         if ([groupInfoD checkArticleIsRead:i]==NO) {
  312.             [self commandHead:i articleDB:articleDB];
  313.         }
  314.         [iPercentageView displayValue:(float)i];
  315.         }
  316.  
  317. endModal:
  318.         [oGetArticleNumWindow orderOut:self];
  319.         [NXApp endModalSession:&theSession];
  320.     } else {
  321.         // article is not so much in server, so will not make any panel
  322.         // for alert the user
  323.         DPSStartWaitCursorTimer();
  324.         DPSFlush();
  325.         for (i=first; i<=last; i++) {
  326.         if ([groupInfoD checkArticleIsRead:i]==NO) {
  327.             [self commandHead:i articleDB:articleDB];
  328.         }
  329.         }
  330.     }
  331.     break;
  332.     }
  333. //    /* add groupInfoD to each articleItem */
  334. //    for (i=0; i<[iArticleDB dataCount]; i++) {
  335. //    [[iArticleDB objectAt:i] insertKeyedObject:groupInfoD];
  336. //    }
  337.  
  338.     [self saveNewsRcFile];
  339.     return articleDB;
  340. }
  341.  
  342. - okArticleNumWindow:sender
  343. {
  344.     [NXApp stopModal:1];
  345.     return self;
  346. }
  347.  
  348. - cancelArticleNumWindow:sender
  349. {
  350.     [NXApp stopModal:0];
  351.     return self;
  352. }
  353.  
  354. - setOGetArticleNumWindow:kwindow
  355. {
  356.     oGetArticleNumWindow = kwindow;
  357.     return self;
  358. }
  359.  
  360. - setOGetArticleNumField:kfield
  361. {
  362.     oGetArticleNumField = kfield;
  363.     return self;
  364. }
  365.  
  366. - setIPercentageView:kview
  367. {
  368.     iPercentageView = kview;
  369.     return self;
  370. }
  371.  
  372. - (InfoD *)getHeaderByMessageId:(const char *)messageId zone:(NXZone *)zone
  373. {
  374.     return([self getHeader:messageId zone:zone]);
  375. }
  376.  
  377. - (InfoD *)commandHead:(int)knum articleDB:karticleDB
  378. {
  379.     char            articleNoString[LINE_CHR_MAX];
  380.     InfoD        *header;
  381.     IOrderedListD    *articleItem;
  382.    
  383.     sprintf (articleNoString, "%d", knum);
  384.     if ((header = [self getHeader:articleNoString zone:headersZone]) != nil) {
  385.         [header addInfoInt:knum key:ARTICLE_NUM];
  386.         sprintf (articleNoString, "%010d", knum);
  387.         articleItem = [[IOrderedListD allocFromZone:headersZone]
  388.             initWithKey:articleNoString];
  389.         [karticleDB insertKeyedObject:(IKeyedObject *)articleItem];
  390.         [articleItem insertKeyedObject:(IKeyedObject *)header];
  391.         return(header);
  392.     } else {
  393.         return(nil);
  394.     }
  395. }
  396.  
  397. - (InfoD *)getHeader:(const char *)articleTag zone:(NXZone *)zone
  398. {
  399.     char    combuf[512];
  400.     int        statusCode;
  401.     InfoD    *header;
  402.     char        key[256], value[512], *value_p;
  403.     char        buf[512], *buf_p;
  404.     char        ch;
  405.  
  406.     sprintf (combuf, "%s %.506s", "head ", articleTag);
  407.     statusCode = [self issueCommand:combuf];
  408.     switch (statusCode){
  409.     case OK_HEAD:
  410.         break;
  411.     case ERR_NOARTIG:
  412.         DBG(1, fprintf(stderr, "WARNING: InntpTalk: \"%s\" no such article"
  413.             " in this group\n", articleTag););
  414.         fseek (inntpFile, (long)0, SEEK_END);
  415.         return(nil);
  416.         break;
  417.     case ERR_NOART:
  418.         DBG(1, fprintf(stderr, "WARNING: InntpTalk: no such article at all"););
  419.         fseek (inntpFile, (long)0, SEEK_END);
  420.         return(nil);
  421.         break;
  422.     default:
  423.         NXRunAlertPanel(LoStr("NewsBase"),
  424.     LoStr("InntpTalk: head command failed, with status code + %d"),
  425.     NULL,NULL,NULL, statusCode);
  426.         exit(1);
  427.     }
  428.     header = [[InfoD allocFromZone:zone] initWithKey:HEADER_INFO];
  429.     while ((ch = fgetc(inntpFile)) !='.') {
  430.         ungetc (ch, inntpFile);
  431.         fscanf (inntpFile, "%512[^\r]\r\n", buf);
  432.         if ( strchr(buf,':') != NULL ) {
  433.         /* first line of each field */
  434.             sscanf (buf, "%256[^:]", key);
  435.         buf_p = buf;
  436.             while (*(buf_p++) != ':')
  437.                 ;
  438.             buf_p++;                            /* skip one space */
  439.             strncpy (value, buf_p, sizeof(value));
  440.         if ([header addInfoString:value key:key]==NULL) {
  441.         NXRunAlertPanel(LoStr("ERROR"),
  442.         LoStr("error occured during adding InfoD"),
  443.         LoStr("OK"),NULL,NULL);
  444.         continue;
  445.         }
  446.     } else {
  447.         /* if field value has multiple lines, go into here */
  448.         /* try to add value to previous value */
  449.         if ((strlen(value)+strlen(buf)+1) > 511) {
  450.         /* over 512, give up to add following value */
  451.         continue;
  452.         }
  453.         if ((value_p=strchr(value, '\0')) == NULL) {
  454.         NXRunAlertPanel(LoStr("ERROR"),
  455.         LoStr("value has no NULL char"),LoStr("OK"),NULL,NULL);
  456.         continue;
  457.         }
  458.         *(value_p++) = ' '; *value_p = '\0';         /*add space*/
  459.         strncpy (value_p, buf, (sizeof(value)-strlen(value)));
  460.         if ([header addInfoString:value key:key]==NULL) {
  461.         NXRunAlertPanel(LoStr("ERROR"),
  462.         LoStr("error occured during adding InfoD"),
  463.         LoStr("OK"),NULL,NULL);
  464.         continue;
  465.         }
  466.     }        
  467.     }
  468.     fgetc(inntpFile); fgetc(inntpFile);  /* consume '\r' and '\n' */
  469.     fseek (inntpFile, (long)0, SEEK_END);
  470.     return(header);
  471. }
  472.  
  473.  
  474.  
  475. - (BOOL)sendArticle:(const char *)messageId
  476. {
  477.     char        combuf[LINE_CHR_MAX];
  478.     int         statusCode;
  479.     char        ch[5];
  480.     NXStream    *stream;
  481.     int         streamLength;
  482.     const char  *streamBuffer;
  483.     int         length, maxLength;
  484.     port_t      port;
  485.     int         returnCode;
  486.     NXStream    *convertedStream;
  487.     extern void    j2e_conv();
  488.  
  489.     if (messageId == NULL) {
  490.         NXRunAlertPanel(LoStr("WARNING"),
  491.     LoStr("InntpTalk: can't find message_id"),LoStr("OK"),NULL,NULL);
  492.         fseek (inntpFile, (long)0, SEEK_END);
  493.         return(NO);
  494.     }
  495.  
  496.     sprintf (combuf, "article %.504s", messageId);
  497.     statusCode = [self issueCommand:combuf];
  498.     if (statusCode != OK_ARTICLE) {
  499.         switch (statusCode){
  500.             case ERR_NOARTIG:
  501.                 NXRunAlertPanel(LoStr("WARNING"),
  502.         LoStr("InntpTalk: no such article in this group"),
  503.         LoStr("OK"),NULL,NULL);
  504.                 fseek (inntpFile, (long)0, SEEK_END);
  505.                 return(NO);
  506.                 break;
  507.             case ERR_NOART:
  508.                 NXRunAlertPanel(LoStr("WARNING"),
  509.         LoStr("InntpTalk: no such article at all"),
  510.         LoStr("OK"),NULL,NULL);
  511.                 fseek (inntpFile, (long)0, SEEK_END);
  512.                 return(NO);
  513.                 break;
  514.             default:
  515.                 NXRunAlertPanel(LoStr("ERROR"),
  516.         LoStr("InntpTalk: body command failed"),LoStr("OK"),NULL,NULL);
  517.                 DBG(1, {
  518.                     char buffer[512];
  519.                     fprintf(stderr, "dump of remaining stream from NNTP "
  520.                         "server follows:\n");
  521.                     while(fgets(buffer, sizeof(buffer), inntpFile) != NULL) {
  522.                         fputs(buffer, stderr);
  523.                     }
  524.                 });
  525.                 fseek (inntpFile, (long)0, SEEK_END);
  526.                 return(NO);
  527.                 break;
  528.             }
  529.     }
  530.     stream = NXOpenMemory(NULL, 0, NX_READWRITE);
  531.     ch[0] = '\r';   
  532.     ch[1] = '\n';
  533.     ch[2] = fgetc(inntpFile);
  534.     ch[3] = fgetc(inntpFile);
  535.     ch[4] = fgetc(inntpFile);
  536.     while (!(ch[0]=='\r' && ch[1]=='\n' && ch[2]=='.' && ch[3]=='\r' && ch[4]=='\n')) {
  537.         NXPutc (stream, ch[2]);
  538.         ch[0] = ch[1];
  539.         ch[1] = ch[2];
  540.         ch[2] = ch[3];
  541.         ch[3] = ch[4];
  542.         ch[4] = fgetc (inntpFile);
  543.     }
  544.     
  545.     // check kanji code on server and convert 
  546.     if (strcmp(NXGetDefaultValue(OWNER,KANJICODE),"JIS")==0) {
  547.         NXSeek(stream, (long)0,NX_FROMSTART);
  548.     convertedStream = NXOpenMemory(NULL, 0, NX_READWRITE);
  549.     j2e_conv(stream, convertedStream);
  550.     NXCloseMemory(stream, NX_FREEBUFFER);
  551.     stream = convertedStream;
  552.     }
  553.     
  554.     NXPutc (stream, '\0');
  555.     streamLength = NXTell(stream);
  556.     streamLength -= 1;        // ### REWRITE ###  for making sure 
  557.                     // '\0' is really not needed any more.
  558.     NXGetMemoryBuffer(stream, &streamBuffer, &length, &maxLength);
  559.     if ((port = NXPortFromName(MMEDITOR, NULL)) == PORT_NULL) {
  560.         NXRunAlertPanel([self name],LoStr("%s is an unknown port."),
  561.             LoStr("OK"), NULL, NULL, MMEDITOR);
  562.     }
  563.     [[NXApp appSpeaker] setSendPort:port];
  564.     if ((returnCode = [[NXApp appSpeaker] receiveArticle:streamBuffer
  565.         length:streamLength]) != 0) {
  566.         NXRunAlertPanel([self name],LoStr("cannot contact port %s."),
  567.             LoStr("OK"), NULL, NULL, MMEDITOR);
  568.     }
  569.     
  570.     DBG(20, write(creat("readStream", 0666), streamBuffer, streamLength));
  571.  
  572.     NXCloseMemory(stream, NX_FREEBUFFER);
  573.     fseek (inntpFile, (long)0, SEEK_END);
  574.     return(YES);
  575. }
  576.  
  577. - (BOOL)canPost
  578. {
  579. //    if (*(char *)[[iCurrentNewsgroup link] infoForKey:CANPOST] == 'y') {
  580.     if (*(char *)[[iCurrentNewsgroup dataForKey:GROUPINFO] 
  581.                         infoForKey:CANPOST] == 'y') {    
  582.         return YES;
  583.     } else {
  584.         return NO;
  585.     }
  586. }
  587.  
  588. - (BOOL)postArticle:(const char *)data length:(int)length
  589. {
  590.     int         statusCode;
  591.     id postingAlertPanel;
  592.     NXModalSession postingModalSession;
  593.     NXStream    *stream, *convertedStream = NULL;
  594.     const char    *buf;
  595.     int        max, len;
  596.     int        bytesSent;
  597.     extern void e2j_conv_adj();
  598.  
  599.     DBG(1, fprintf(stderr, "length = %d\n*** start of article ***\n%.10000s\n"
  600.         "*** end of article ***\n", length, data));
  601.     DBG(20, write(creat("postStream", 0666), data, length));
  602.     
  603.     // check for duplicate message id
  604. /*
  605.     if ((header = [self getHeaderByMessageId:messageId
  606.         zone:NXDefaultMallocZone()]) != nil) {
  607.         NXRunAlertPanel(LoStr("NewsBase"),
  608.                 LoStr("Message-ID already exists on NNTP server"),
  609.                 NULL,NULL,NULL);
  610.         free(header);
  611.         return(NO);
  612.     }
  613. */
  614.     switch(NXRunAlertPanel(LoStr("NewsBase"),
  615.     LoStr("Post to NNTP Server"),LoStr("Confirm"),LoStr("Cancel"),NULL)) {
  616.     case NX_ALERTDEFAULT:
  617.         break;
  618.     case NX_ALERTALTERNATE:
  619.         return(NO);
  620.     }
  621.  
  622.     statusCode = [self issueCommand:"POST"];
  623.     fseek(inntpFile, (long)0, SEEK_END);
  624.     switch (statusCode) {
  625.     case CONT_POST:
  626.         break; 
  627.     case ERR_NOPOST:
  628.         NXRunAlertPanel(LoStr("NewsBase"),
  629.         LoStr("POST command failed.  %d  posting not allowed."),
  630.         NULL,NULL,NULL, statusCode);
  631.         return(NO);
  632.     default:
  633.         // This should never occur!
  634.         NXRunAlertPanel(LoStr("NewsBase"),
  635.     LoStr("POST command failed. %d is status code returned by NNTP server.")    ,NULL,NULL,NULL, statusCode);
  636.         return(NO);
  637.     }
  638.  
  639.     postingAlertPanel = NXGetAlertPanel(LoStr("NewsBase"),
  640.         LoStr("Posting article to NNTP Server... Please wait.")
  641.     ,NULL, NULL, NULL);
  642.     [NXApp beginModalSession:&postingModalSession for:postingAlertPanel];
  643.     
  644.     // copy input data
  645.     stream = NXOpenMemory(data, length, NX_READONLY);
  646.     NXSeek(stream, (long)0,NX_FROMSTART);
  647.     convertedStream = NXOpenMemory(NULL, 0, NX_READWRITE);
  648.  
  649.     // check kanji code on server and convert 
  650.     if (strcmp(NXGetDefaultValue(OWNER,KANJICODE),"JIS")==0) {
  651.     e2j_conv_adj(stream, convertedStream);
  652.     } else {
  653.     e2e_adj(stream, convertedStream);
  654.     }
  655.     // convert stream to byte stream
  656.     NXGetMemoryBuffer(convertedStream, &buf, &len, &max);
  657.     NXCloseMemory(stream, NX_FREEBUFFER);
  658.  
  659.     while (len > 0) {
  660.         bytesSent = send(nntpSocket, buf, len, 0);
  661.         buf += bytesSent;
  662.         len -= bytesSent;
  663.     }
  664.     if (convertedStream != NULL) {
  665.     NXCloseMemory(convertedStream, NX_FREEBUFFER);
  666.     }
  667.     fseek(inntpFile, (long)0, SEEK_END);
  668.     statusCode = [self _getResponse];
  669.     fseek (inntpFile, (long)0, SEEK_END);
  670.     [NXApp endModalSession:&postingModalSession];
  671.     [postingAlertPanel orderOut:self];
  672.     NXFreeAlertPanel(postingAlertPanel);
  673.     
  674.     switch (statusCode) {
  675.     case OK_POSTED:
  676. /*
  677.         // check to make sure
  678.         if ((header = [self getHeaderByMessageId:messageId
  679.             zone:NXDefaultMallocZone()]) != nil) {
  680.             NXRunAlertPanel(LoStr("NewsBase"),
  681.         LoStr("Posting has been confirmed with NNTP server."),
  682.         NULL,NULL,NULL);
  683.             free(header);
  684.             return(YES);
  685.         } else {
  686. //          B News Server will not return header
  687. //          NXRunAlertPanel(LoStr("NewsBase"),LoStr("Posting failed! check size/newssgroup name."),NULL,NULL,NULL);
  688. //          return(NO);
  689.             return(YES);
  690.         }
  691. */
  692.         return(YES);
  693.     case ERR_POSTFAIL:
  694.         NXRunAlertPanel(LoStr("NewsBase"),
  695.         LoStr("POST command failed.  %d  posting failed."),
  696.         NULL,NULL,NULL, statusCode);
  697.         return(NO);
  698.     default:
  699.         // This should never occur!
  700.         NXRunAlertPanel(LoStr("NewsBase"),    
  701.     LoStr("POST command failed. %d is status code returned by NNTP server.")    ,NULL,NULL,NULL, statusCode);
  702.         return(NO);
  703.     }
  704. }
  705.  
  706. - readNewsRcFile
  707. {
  708.     const char    *newsrc_file;
  709.                     /* "" = $HOME/.newsrc */
  710.     newsrc_file = NXGetDefaultValue(OWNER,NEWSRCFILE);
  711.     if ((iNewsRc=[[INewsRc allocFromZone:
  712.             (NXZone *)newsgroupTreeZone] initFile:newsrc_file])==NULL) {
  713.     return NULL;
  714.     }
  715.     [iNewsRc readRcfile];
  716.     return self;
  717. }
  718.  
  719. - (void)saveNewsRcFile
  720. {
  721.     [iNewsRc saveToRcfileFrom:iNewsGroupTreeRoot];
  722. }
  723.  
  724. - free
  725. {
  726.     NXDestroyZone(newsgroupTreeZone);
  727.     return([super free]);
  728. }
  729.  
  730. static void reconnectEvent(DPSTimedEntry teNum, double now, void * nntp)
  731. {
  732. #ifdef DEBUG
  733.     fprintf(stderr," ++++reconnecting to nntp server\n");
  734. #endif
  735.  
  736.     if ([(id)nntp reconnectServer] == nil) {
  737.     // reconnectServer is inplemented in "INNTP"
  738.     // only make new connection and not issue "list" command
  739.     NXRunAlertPanel(LoStr("NewsBase"),
  740.         LoStr("Can not auto recconect to nntp server."),
  741.         LoStr("OK"),NULL,NULL);
  742.     [NXApp terminate:nntp];
  743.     }
  744.     return;
  745. }
  746.  
  747. - reconnectServer:sender
  748. {
  749.     // called from menu and make new tree by "list" command
  750.     [super reconnectServer];
  751.     [self listAndMakeNewsgroupTree];
  752.     return self;
  753. }
  754.  
  755. - setTimedEntryForReconnect
  756. {
  757.     double    interval;
  758.     int        priority = 1;
  759.     
  760.     interval = (double)atof(NXGetDefaultValue(OWNER,RECONNECTTIME)) * 60.0;
  761.     DPSAddTimedEntry (interval, reconnectEvent, (id)self, priority);
  762.     return self;
  763. }
  764.  
  765. - cancelArticleMessageID:(const char *)messageID from:(const char *)fromValue
  766. {
  767.     NXStream    *articlestream;
  768.     int len,max,size;
  769.     char *buff;
  770.     const char     *cancelHeader = "Newsgroups: control\r\nSubject: cancel\r\ncontrol: cancel ";
  771.     
  772.     // From field should have been checked before this method.
  773.     
  774.     articlestream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  775.     NXPrintf(articlestream,"%s %s\r\nFrom: %s\r\n.\r\n", 
  776.                     cancelHeader, messageID, fromValue);
  777.     NXSeek(articlestream, (long)0,NX_FROMEND);
  778.     (long)size = NXTell(articlestream);
  779.      NXGetMemoryBuffer(articlestream,&buff, &len,  &max);
  780.     [self postArticle:(const char *)buff length:(int)size];
  781.      NXCloseMemory(articlestream,NX_FREEBUFFER);
  782.     
  783.     return(self);
  784. }
  785.     
  786. @end
  787.